home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / mint96sb.zoo / src / mem.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-15  |  30.1 KB  |  1,339 lines

  1. /*
  2. Copyright 1990,1991,1992 Eric R. Smith. All rights reserved.
  3. */
  4.  
  5. /*
  6.  * mem.c:: routines for managing memory regions
  7.  */
  8.  
  9. #include "mint.h"
  10.  
  11. static long core_malloc P_((long, int));
  12.  
  13. /* macro for testing whether a memory region is free */
  14. #define ISFREE(m) ((m)->links == 0)
  15.  
  16. /*
  17.  * list of shared text regions currently being executed
  18.  */
  19. SHTEXT *text_reg = 0;
  20.  
  21. /*
  22.  * initialize memory routines
  23.  */
  24.  
  25. /* initial number of memory regions */
  26. #define NREGIONS 512
  27.  
  28. /* number of new regions to allocate when the initial ones are used up */
  29. #define NEWREGIONS 256
  30.  
  31. static MEMREGION use_regions[NREGIONS+1];
  32. MEMREGION *rfreelist;
  33.  
  34. void
  35. init_mem()
  36. {
  37.     int i;
  38.  
  39.     use_regions[NREGIONS].next = 0;
  40.     for (i = 0; i < NREGIONS; i++) {
  41.         use_regions[i].next = &use_regions[i+1];
  42.     }
  43.     rfreelist = use_regions;
  44.  
  45.     init_core();
  46.     init_swap();
  47. }
  48.  
  49. /*
  50.  * init_core(): initialize the core memory map (normal ST ram) and also
  51.  * the alternate memory map (fast ram on the TT)
  52.  */
  53.  
  54. static MEMREGION *_core_regions = 0, *_alt_regions = 0,
  55.     *_ker_regions = 0;
  56.  
  57. MMAP core = &_core_regions;
  58. MMAP alt = &_alt_regions;
  59. MMAP ker = &_ker_regions;
  60.  
  61. /* note: add_region must adjust both the size and starting
  62.  * address of the region being added so that memory is
  63.  * always properly aligned
  64.  */
  65.  
  66. int
  67. add_region(map, place, size, mflags)
  68.     MMAP map;
  69.     ulong place, size;
  70.     unsigned mflags;    /* initial flags for region */
  71. {
  72.     MEMREGION *m;
  73.     ulong newplace;
  74.  
  75.     newplace = ROUND(place);
  76.     size = (place + size) - newplace;
  77.     size &= ~MASKBITS;
  78.     if ((long)size <= 0L)    /* region too small to use */
  79.         return 1;
  80.  
  81.     m = new_region();
  82.     if (m == 0)
  83.         return 0;    /* failure */
  84.     m->links = 0;
  85.     m->len = size;
  86.     m->loc = newplace;
  87.     m->next = *map;
  88.     m->mflags = mflags;
  89.     *map = m;
  90.     return 1;    /* success */
  91. }
  92.  
  93. static long
  94. core_malloc(amt, mode)
  95.     long amt;
  96.     int mode;
  97. {
  98.     static int mxalloc = -1;    /* does GEMDOS know about Mxalloc? */
  99.     long ret;
  100.  
  101.     if (mxalloc < 0) {
  102.         ret = (long)Mxalloc(-1L, 0);
  103.         if (ret == -32) mxalloc = 0;    /* unknown function */
  104.         else if (ret >= 0) mxalloc = 1;
  105.         else {
  106.             ALERT("GEMDOS returned %ld from Mxalloc", ret);
  107.             mxalloc = 0;
  108.         }
  109.     }
  110.     if (mxalloc)
  111.         return Mxalloc(amt, mode);
  112.     else if (mode == 1)
  113.         return 0L;
  114.     else
  115.         return Malloc(amt);
  116. }
  117.  
  118. void
  119. init_core()
  120. {
  121.     ulong size;
  122.     ulong place;
  123.     void *tossave;
  124.  
  125.     tossave = (void *)core_malloc((long)TOS_MEM, 0);
  126.     if (!tossave) {
  127.         FATAL("Not enough memory to run MiNT");
  128.     }
  129.  
  130. /* initialize kernel memory */
  131.     place = (ulong)core_malloc(KERNEL_MEM, 3);
  132.     if (place != 0) {
  133.         (void)add_region(ker, place, KERNEL_MEM, M_KER);
  134.     }
  135.  
  136. /* initialize ST RAM */
  137.     size = (ulong)core_malloc(-1L, 0);
  138.     while (size > 0) {
  139.         place = (ulong)core_malloc(size, 0);
  140.         if (!add_region(core, place, size, M_CORE))
  141.             FATAL("init_mem: unable to add a region");
  142.         size = (ulong)core_malloc(-1L, 0);
  143.     }
  144.  
  145. /* initialize alternate RAM */
  146.     size = (ulong)core_malloc(-1L, 1);
  147.     while (size > 0) {
  148.         place = (ulong)core_malloc(size, 1);
  149.         if (!add_region(alt, place, size, M_ALT))
  150.             FATAL("init_mem: unable to add a region");
  151.         size = (ulong)core_malloc(-1L, 1);
  152.     }
  153.  
  154.     (void)Mfree(tossave);        /* leave some memory for TOS to use */
  155. }
  156.  
  157. /*
  158.  * init_swap(): initialize the swap area; for now, this does nothing
  159.  */
  160.  
  161. MEMREGION *_swap_regions = 0;
  162. MMAP swap = &_swap_regions;
  163.  
  164. void
  165. init_swap()
  166. {
  167. }
  168.  
  169. /*
  170.  * routines for allocating/deallocating memory regions
  171.  */
  172.  
  173. /*
  174.  * new_region returns a new memory region descriptor, or NULL
  175.  */
  176.  
  177. MEMREGION *
  178. new_region()
  179. {
  180.     MEMREGION *m, *newfrees;
  181.     int i;
  182.  
  183.     m = rfreelist;
  184.     if (!m) {
  185.         ALERT("new_region: ran out of free regions");
  186.         return 0;
  187.     }
  188.     assert(ISFREE(m));
  189.     rfreelist = m->next;
  190.     m->next = 0;
  191.  
  192. /* if we're running low on free regions, allocate some more
  193.  * we have to do this with at least 1 free region left so that get_region
  194.  * has a chance of working
  195.  */
  196.     if (rfreelist && !rfreelist->next) {
  197.         MEMREGION *newstuff;
  198.  
  199.         TRACE(("get_region: getting new region descriptors"));
  200.         newstuff = get_region(ker, NEWREGIONS*SIZEOF(MEMREGION));
  201.         if (!newstuff)
  202.             newstuff = get_region(alt,NEWREGIONS*SIZEOF(MEMREGION));
  203.         if (!newstuff)
  204.             newstuff = get_region(core, NEWREGIONS*SIZEOF(MEMREGION));
  205.         newfrees = newstuff ? (MEMREGION *)newstuff->loc : 0;
  206.         if (newfrees) {
  207.             newfrees[NEWREGIONS-1].next = 0;
  208.             newfrees[NEWREGIONS-1].links = 0;
  209.             for (i = 0; i < NEWREGIONS-1; i++) {
  210.                 newfrees[i].next = &newfrees[i+1];
  211.                 newfrees[i].links = 0;
  212.             }
  213.             rfreelist = newfrees;
  214.         } else {
  215.             DEBUG(("couldn't get new region descriptors!"));
  216.         }
  217.     }
  218.  
  219.     return m;
  220. }
  221.  
  222. /*
  223.  * dispose_region destroys a memory region descriptor
  224.  */
  225.  
  226. void
  227. dispose_region(m)
  228.     MEMREGION *m;
  229. {
  230.     m->next = rfreelist;
  231.     rfreelist = m;
  232. }
  233.  
  234. /*
  235.  * virtaddr
  236.  * attach_region(proc, reg): attach the region to the given process:
  237.  * returns the address at which it was attached, or NULL if the process
  238.  * cannot attach more regions. The region link count is incremented if
  239.  * the attachment is successful.
  240.  */
  241.  
  242. virtaddr
  243. attach_region(proc, reg)
  244.     PROC *proc;
  245.     MEMREGION *reg;
  246. {
  247.     int i;
  248.     MEMREGION **newmem;
  249.     virtaddr *newaddr;
  250.  
  251.     if (!reg || !reg->loc) {
  252.         ALERT("attach_region: attaching a null region??");
  253.         return 0;
  254.     }
  255.     for (i = 0; i < proc->num_reg; i++) {
  256.         if (!proc->mem[i]) {
  257.             assert(proc->addr[i] == 0);
  258.             reg->links++;
  259.             proc->mem[i] = reg;
  260.             proc->addr[i] = (virtaddr) reg->loc;
  261.             return proc->addr[i];
  262.         }
  263.     }
  264.  
  265. /* Hmmm, OK, we have to expand the process' memory table */
  266.     TRACE(("Expanding process memory table"));
  267.     i = proc->num_reg + NUM_REGIONS;
  268.  
  269.     newmem = kmalloc(i * SIZEOF(MEMREGION *));
  270.     newaddr = kmalloc(i * SIZEOF(virtaddr));
  271.  
  272.     if (newmem && newaddr) {
  273.     /* copy over the old address mapping */
  274.         for (i = 0; i < proc->num_reg; i++) {
  275.             newmem[i] = proc->mem[i];
  276.             newaddr[i] = proc->addr[i];
  277.             if (newmem[i] == 0)
  278.                 assert(newaddr[i] == 0);
  279.         }
  280.     /* initialize the rest of the tables */
  281.         for(; i < proc->num_reg + NUM_REGIONS; i++) {
  282.             newmem[i] = 0;
  283.             newaddr[i] = 0;
  284.         }
  285.     /* free the old tables */
  286.         kfree(proc->mem); kfree(proc->addr);
  287.         proc->mem = newmem;
  288.         proc->addr = newaddr;
  289.         proc->num_reg += NUM_REGIONS;
  290.     /* this call will succeed */
  291.         TRACE(("recursively calling attach_region"));
  292.         return attach_region(proc, reg);
  293.     }
  294.  
  295.     DEBUG(("attach_region: failed"));
  296.     return 0;
  297. }
  298.  
  299. /*
  300.  * detach_region(proc, reg): remove region from the procedure's address
  301.  * space. If no more processes reference the region, return it to the
  302.  * system. Note that we search backwards, so that the most recent
  303.  * attachment of memory gets detached!
  304.  */
  305.  
  306. void
  307. detach_region(proc, reg)
  308.     PROC *proc;
  309.     MEMREGION *reg;
  310. {
  311.     int i;
  312.  
  313.     if (!reg) return;
  314.     for (i = proc->num_reg - 1; i >= 0; i--) {
  315.         if (proc->mem[i] == reg) {
  316.             reg->links--;
  317.             proc->mem[i] = 0; proc->addr[i] = 0;
  318.             if (reg->links == 0) {
  319.                 free_region(reg);
  320.             }
  321.             return;
  322.         }
  323.     }
  324.     DEBUG(("detach_region: region not attached"));
  325. }
  326.  
  327. /*
  328.  * get_region(MMAP map, ulong size) -- allocate a new region of the
  329.  * given size in the given memory map. if no region big enough is available,
  330.  * return NULL, otherwise return a pointer to the region.
  331.  * the "links" field in the region is set to 1
  332.  *
  333.  * BEWARE: new_region may call get_region (indirectly), so we have to be
  334.  * _very_ careful with re-entrancy in this function
  335.  */
  336.  
  337. MEMREGION *
  338. get_region(map, size)
  339.     MMAP map;
  340.     ulong size;
  341. {
  342.     MEMREGION *m, *n;
  343.  
  344. /* precautionary measures */
  345.     if (size == 0) {
  346.         DEBUG(("request for 0 bytes??"));
  347.         size = 1;
  348.     }
  349.  
  350.     size = ROUND(size);
  351.  
  352.     n = *map;
  353.  
  354.     sanity_check(map);
  355. /* exact matches are likely to be rare, so we pre-allocate a new
  356.  * region here; this helps us to avoid re-entrancy problems
  357.  * when new_region calls get_region
  358.  */
  359.     m = new_region();
  360.  
  361.     while (n) {
  362.         if (ISFREE(n)) {
  363.             if (n->len == size) {
  364.                 if (m) dispose_region(m);
  365.                 n->links++;
  366.                 return n;
  367.             }
  368.             else if (n->len > size) {
  369. /* split a new region, 'm', which will contain the free bytes after n */
  370.                 if (m) {
  371.                     m->next = n->next;
  372.                     n->next = m;
  373.                     m->mflags = n->mflags & M_MAP;
  374.                     m->loc = n->loc + size;
  375.                     m->len = n->len - size;
  376.                     n->len = size;
  377.                     n->links++;
  378.                     return n;
  379.                 } else {
  380.                     DEBUG(("get_region: no regions left"));
  381.                     return 0;
  382.                 }
  383.             }
  384.         }
  385.         n = n->next;
  386.     }
  387.  
  388.     if (m)
  389.         dispose_region(m);
  390.     return NULL;
  391. }
  392.  
  393. /*
  394.  * free_region(MEMREGION *reg): free the indicated region. The map
  395.  * in which the region is contained is given by reg->mflags.
  396.  * the caller is responsible for making sure that the region
  397.  * really should be freed, i.e. that reg->links == 0.
  398.  *
  399.  * special things to do:
  400.  * if the region is a shared text region, we must close the
  401.  * associated file descriptor
  402.  */
  403.  
  404. void
  405. free_region(reg)
  406.     MEMREGION *reg;
  407. {
  408.     MMAP map;
  409.     MEMREGION *m;
  410.     SHTEXT *s, **old;
  411.  
  412.     if (!reg) return;
  413.  
  414.     assert(ISFREE(reg));
  415.  
  416.     if (reg->mflags & M_SHTEXT) {
  417.         TRACE(("freeing shared text region"));
  418.         old = &text_reg;
  419.         for(;;) {
  420.             s = *old;
  421.             if (!s) break;
  422.             if (s->text == reg) {
  423.                 do_close(s->f);
  424.                 *old = s->next;
  425.                 kfree(s);
  426.                 break;
  427.             }
  428.             old = &s->next;
  429.         }
  430.         if (!s) {
  431.             DEBUG(("No shared text entry for M_SHTEXT region??"));
  432.         }
  433.     }
  434.  
  435.     if (reg->mflags & M_CORE)
  436.         map = core;
  437.     else if (reg->mflags & M_ALT)
  438.         map = alt;
  439.     else if (reg->mflags & M_KER)
  440.         map = ker;
  441.     else {
  442.         FATAL("free_region: region flags not valid (%x)", reg->mflags);
  443.     }
  444.     reg->mflags &= M_MAP;
  445.     m = *map;
  446.     assert(m);
  447.  
  448. /* BUG: should invalidate caches entries - a copyback cache could stuff
  449.  * things into freed memory.
  450.  *    cinv(m->loc, m->len);
  451.  */
  452.     if (m == reg) goto merge_after;
  453.  
  454. /* merge previous region if it's free and contiguous with 'reg' */
  455.  
  456. /* first, we find the region */
  457.     while (m && m->next != reg)
  458.         m = m->next;
  459.  
  460.     if (m == NULL) {
  461.         FATAL("couldn't find region %lx: loc: %lx len: %ld",
  462.             reg, reg->loc, reg->len);
  463.     }
  464.  
  465.     if (ISFREE(m) && (m->loc + m->len == reg->loc)) {
  466.         m->len += reg->len;
  467.         assert(m->next == reg);
  468.         m->next = reg->next;
  469.         reg->next = 0;
  470.         dispose_region(reg);
  471.         reg = m;
  472.     }
  473.  
  474. /* merge next region if it's free and contiguous with 'reg' */
  475. merge_after:
  476.     m = reg->next;
  477.     if (m && ISFREE(m) && reg->loc + reg->len == m->loc) {
  478.         reg->len += m->len;
  479.         reg->next = m->next;
  480.         m->next = 0;
  481.         dispose_region(m);
  482.     }
  483.  
  484.     sanity_check(map);
  485. }
  486.  
  487. /*
  488.  * shrink_region(MEMREGION *reg, ulong newsize):
  489.  *   shrink region 'reg', so that it is now 'newsize' bytes long.
  490.  *   if 'newsize' is bigger than the region's current size, return EGSBF;
  491.  *   otherwise return 0.
  492.  */
  493.  
  494. long
  495. shrink_region(reg, newsize)
  496.     MEMREGION *reg;
  497.     ulong newsize;
  498. {
  499.     MEMREGION *n;
  500.     ulong diff;
  501.  
  502.  
  503.     newsize = ROUND(newsize);
  504.  
  505.     assert(reg->links > 0);
  506.  
  507.     if (!(reg->mflags & (M_CORE | M_ALT | M_KER))) {
  508.         FATAL("shrink_region: bad region flags (%x)", reg->mflags);
  509.     }
  510.  
  511. /* shrinking to 0 is the same as freeing */
  512.     if (newsize == 0) {
  513.         detach_region(curproc, reg);
  514.         return 0;
  515.     }
  516.  
  517. /* if new size is the same as old size, don't do anything */
  518.     if (newsize == reg->len) {
  519.         return 0;    /* nothing to do */
  520.     }
  521.  
  522.     if (newsize > reg->len) {
  523.         DEBUG(("shrink_region: request to make region bigger"));
  524.         return EGSBF;    /* growth failure */
  525.     }
  526.  
  527. /* OK, we're going to free (reg->len - newsize) bytes at the end of
  528.    this block. If the block after us is already free, simply add the
  529.    space to that block.
  530.  */
  531.     n = reg->next;
  532.     diff = reg->len - newsize;
  533.  
  534.     if (n && ISFREE(n) && reg->loc + reg->len == n->loc) {
  535.         reg->len = newsize;
  536.         n->loc -= diff;
  537.         n->len += diff;
  538.         return 0;
  539.     }
  540.     else {
  541.         n = new_region();
  542.         if (!n) {
  543.             DEBUG(("shrink_region: new_region failed"));
  544.             return EINTRN;
  545.         }
  546.         reg->len = newsize;
  547.         n->loc = reg->loc + newsize;
  548.         n->len = diff;
  549.         n->mflags = reg->mflags & M_MAP;
  550.         n->next = reg->next;
  551.         reg->next = n;
  552.     }
  553.     return 0;
  554. }
  555.  
  556. /*
  557.  * max_rsize(map): return the length of the biggest free region
  558.  * in the given memory map, or 0 if no regions remain.
  559.  */
  560.  
  561. long
  562. max_rsize(map)
  563.     MMAP map;
  564. {
  565.     MEMREGION *m;
  566.     long size = 0;
  567.  
  568.     for (m = *map; m; m = m->next) {
  569.         if (ISFREE(m)) {
  570.             if (m->len > size) {
  571.                 size = m->len;
  572.             }
  573.         }
  574.     }
  575.     return size;
  576. }
  577.  
  578. /*
  579.  * tot_rsize(map, flag): if flag == 1, return the total number of bytes in
  580.  * the given memory map; if flag == 0, return only the number of free
  581.  * bytes
  582.  */
  583.  
  584. long
  585. tot_rsize(map, flag)
  586.     MMAP map;
  587.     int flag;
  588. {
  589.     MEMREGION *m;
  590.     long size = 0;
  591.  
  592.     for (m = *map; m; m = m->next) {
  593.         if (flag || ISFREE(m)) {
  594.             size += m->len;
  595.         }
  596.     }
  597.     return size;
  598. }
  599.  
  600. /*
  601.  * alloc_region(MMAP map, ulong size): allocate a new region and attach
  602.  * it to the current process; returns the address at which the region
  603.  * was attached, or NULL. If not enough memory is found, wait a bit
  604.  * and try again before giving up (maybe someone else will free some
  605.  * memory)
  606.  */
  607.  
  608. virtaddr
  609. alloc_region(map, size)
  610.     MMAP map;
  611.     ulong size;
  612. {
  613.     MEMREGION *m;
  614.     PROC *proc = curproc;
  615.     virtaddr v;
  616.  
  617.     m = get_region(map, size);
  618.     if (!m) {
  619.         return 0;
  620.     }
  621.  
  622. /* sanity check: even addresses only, please */
  623.     assert((m->loc & MASKBITS) == 0);
  624.  
  625.     v = attach_region(proc, m);
  626. /* NOTE: get_region returns a region with link count 1; since attach_region
  627.  * increments the link count, we restore it after calling attach_region
  628.  */
  629.     m->links = 1;
  630.     if (!v) {
  631.         m->links = 0;
  632.         free_region(m);
  633.         return 0;
  634.     }
  635.     return v;
  636. }
  637.  
  638. /*
  639.  * routines for creating a copy of an environment, and a new basepage.
  640.  * note that the memory regions created should immediately be attached to
  641.  * a process! Also note that create_env always operates in ST RAM, but
  642.  * create_base might not.
  643.  */
  644.  
  645. MEMREGION *
  646. create_env(env)
  647.     const char *env;
  648. {
  649.     long size;
  650.     MEMREGION *m;
  651.     virtaddr v;
  652.     const char *old;
  653.     char *new;
  654.     
  655.     if (!env) {
  656.         env = ((BASEPAGE *)curproc->base)->p_env;
  657.             /* duplicate parent's environment */
  658.     }
  659.     size = 2;
  660.     old = env;
  661.     while (*env || *(env+1))
  662.         env++,size++;
  663.     v = alloc_region(core, size);
  664.     if (!v) {
  665.         DEBUG(("create_env: alloc_region failed"));
  666.         return (MEMREGION *)0;
  667.     }
  668.     m = addr2mem(v);
  669.  
  670. /* copy the old environment into the new */
  671.     new = (char *) m->loc;
  672.     while (size > 0) {
  673.         *new++ = *old++;
  674.         --size;
  675.     }
  676.     return m;
  677. }
  678.  
  679. MEMREGION *
  680. create_base(cmd, env, flags, prgsize)
  681.     const char *cmd;
  682.     MEMREGION *env;
  683.     ulong flags, prgsize;
  684. {
  685.     long len, coresize, altsize;
  686.     MMAP map;
  687.     MEMREGION *m;
  688.     BASEPAGE *b;
  689.  
  690. /* if flags & F_ALTLOAD == 1, then we might decide to load in alternate
  691.    RAM if enough is available. "enough" is: if more alt ram than ST ram,
  692.    load there; otherwise, if more than (minalt+1)*128K alt ram available
  693.    for heap space, load in alt ram ("minalt" is the high byte of flags)
  694.  */
  695.     if (flags & F_ALTLOAD) {
  696.         coresize = max_rsize(core);
  697.         altsize = max_rsize(alt);
  698.         if (altsize >= coresize)
  699.             map = alt;
  700.         else {
  701.             len = (flags & F_MINALT) >> 28L;
  702.             len = (len+1)*128*1024L + prgsize + 256;
  703.             if (altsize >= len)
  704.                 map = alt;
  705.             else
  706.                 map = core;
  707.         }
  708.     }
  709.     else
  710.         map = core;
  711.  
  712.     len = max_rsize(map);
  713.  
  714. /* make sure that a little bit of memory is left over */
  715.     if (len > 2*KEEP_MEM) {
  716.         len -= KEEP_MEM;
  717.     }
  718.     m = addr2mem(alloc_region(map, len));
  719.     if (!m) {
  720.         DEBUG(("create_base: alloc_region failed"));
  721.         return 0;
  722.     }
  723.     b = (BASEPAGE *)(m->loc);
  724.  
  725.     zero((char *)b, (long)sizeof(BASEPAGE));
  726.     b->p_lowtpa = (long)b;
  727.     b->p_hitpa = m->loc + m->len;
  728.     b->p_env = (char *)env->loc;
  729.     b->p_flags = flags;
  730.  
  731.     if (cmd)
  732.         strncpy(b->p_cmdlin, cmd, 126);
  733.     return m;
  734. }
  735.  
  736. /*
  737.  * load_region(): loads the program with the given file name
  738.  * into a new region, and returns a pointer to that region. On
  739.  * an error, returns 0 and leaves the error number in mint_errno.
  740.  * "env" points to an already set up environment region, as returned
  741.  * by create_env. "xp" points to the file attributes, which
  742.  * Pexec has already determined. "text" is a pointer to a MEMREGION
  743.  * pointer, which will be set to the region occupied by the shared
  744.  * text segment of this program (if applicable).
  745.  */
  746.  
  747. MEMREGION *
  748. load_region(filename, env, cmdlin, xp, text)
  749.     const char *filename;
  750.     MEMREGION *env;
  751.     const char *cmdlin;
  752.     XATTR *xp;        /* attributes for the file just loaded */
  753.     MEMREGION **text;    /* set to point to shared text region,
  754.                    if any */
  755. {
  756.     FILEPTR *f;
  757.     DEVDRV *dev;
  758.     MEMREGION *reg, *shtext;
  759.     BASEPAGE *b;
  760.     long size, start;
  761.     FILEHEAD fh;
  762.     int trycount;
  763.  
  764. /* bug: this should be O_DENYW mode, not O_DENYNONE */
  765. /* we must use O_DENYNONE because of the desktop and because of the
  766.  * TOS file system brain-damage
  767.  */
  768.     f = do_open(filename, O_DENYNONE | O_EXEC, 0, xp);
  769.     if (!f) {
  770.         return 0;        /* mint_errno set by do_open */
  771.     }
  772.  
  773.     dev = f->dev;
  774.     size = (*dev->read)(f, (void *)&fh, (long)sizeof(fh));
  775.     if (fh.fmagic != GEMDOS_MAGIC || size != (long)sizeof(fh)) {
  776.         DEBUG(("load_region: file not executable"));
  777.         mint_errno = ENOEXEC;
  778. failed:
  779.         do_close(f);
  780.         return 0;
  781.     }
  782.  
  783.     if (fh.flag & F_SHTEXT) {
  784.         TRACE(("loading shared text segment"));
  785.         shtext = get_text_seg(f, &fh, xp);
  786.         if (!shtext) {
  787.             DEBUG(("load_region: unable to get shared text segment"));
  788. /* mint_errno set in get_text_seg */
  789.             goto failed;
  790.         }
  791.         size = fh.fdata + fh.fbss;
  792.     } else {
  793.         size = fh.ftext + fh.fdata + fh.fbss;
  794.         shtext = 0;
  795.     }
  796.  
  797.     reg = 0;
  798.     for (trycount = 0; (trycount < 1) && (reg == 0); trycount++) {
  799.         reg = create_base(cmdlin, env, fh.flag, size);
  800.         if (size+1024L > reg->len) {
  801.             DEBUG(("load_region: insufficient memory to load"));
  802.             detach_region(curproc, reg);
  803.             reg = 0;
  804.         }
  805.         if (!reg) {
  806.     /* maybe the memory shortage is short-term; sleep a bit to see */
  807.             nap(10);
  808.         }
  809.     }
  810.  
  811.     if (reg == 0) {
  812.         if (shtext) {
  813.             detach_region(curproc, shtext);
  814.         }
  815.         mint_errno = ENSMEM;
  816.         goto failed;
  817.     }
  818.  
  819.     b = (BASEPAGE *)reg->loc;
  820.     b->p_flags = fh.flag;
  821.     if (shtext) {
  822.         b->p_tbase = shtext->loc;
  823.         b->p_tlen = 0;
  824.         b->p_dbase = b->p_lowtpa + 256;
  825.     } else {
  826.         b->p_tbase = b->p_lowtpa + 256;
  827.         b->p_tlen = fh.ftext;
  828.         b->p_dbase = b->p_tbase + b->p_tlen;
  829.     }
  830.     b->p_dlen = fh.fdata;
  831.     b->p_bbase = b->p_dbase + b->p_dlen;
  832.     b->p_blen = fh.fbss;
  833.  
  834. /* if shared text, then we start loading at the end of the
  835.  * text region, since that is already set up
  836.  */
  837.     if (shtext) {
  838.     /* skip over text info */
  839.         size = fh.fdata;
  840.         start = fh.ftext;
  841.     } else {
  842.         size = fh.ftext + fh.fdata;
  843.         start = 0;
  844.     }
  845.  
  846.     mint_errno = (int)load_and_reloc(f, &fh, (char *)b+256, start,
  847.             size, b);
  848.  
  849.     if (mint_errno) {
  850.         detach_region(curproc, reg);
  851.         if (shtext) detach_region(curproc, shtext);
  852.         goto failed;
  853.     }
  854.  
  855.     if (fh.flag & F_FASTLOAD)            /* fastload bit */
  856.         size = b->p_blen;
  857.     else
  858.         size = b->p_hitpa - b->p_bbase;
  859.     zero((char *)b->p_bbase, size);
  860.  
  861.     do_close(f);
  862.     *text = shtext;
  863.     return reg;
  864. }
  865.  
  866. /*
  867.  * load_and_reloc(f, fh, where, start, nbytes): load and relocate from
  868.  * the open GEMDOS executable file f "nbytes" bytes starting at offset
  869.  * "start" (relative to the end of the file header, i.e. from the first
  870.  * byte of the actual program image in the file). "where" is the address
  871.  * in (physical) memory into which the loaded image must be placed; it is
  872.  * assumed that "where" is big enough to hold "nbytes" bytes!
  873.  */
  874.  
  875. long
  876. load_and_reloc(f, fh, where, start, nbytes, base)
  877.     FILEPTR *f;
  878.     FILEHEAD *fh;
  879.     char *where;
  880.     long start;
  881.     long nbytes;
  882.     BASEPAGE *base;
  883. {
  884.     unsigned char c, *next;
  885.     long r;
  886.     DEVDRV *dev;
  887. #define LRBUFSIZ 8196
  888.     static unsigned char buffer[LRBUFSIZ];
  889.     long fixup, size, bytes_read;
  890.     long reloc;
  891.  
  892.  
  893. TRACE(("load_and_reloc: %ld to %ld at %lx", start, nbytes+start, where));
  894.     dev = f->dev;
  895.  
  896.     r = (*dev->lseek)(f, start+sizeof(FILEHEAD), SEEK_SET);
  897.     if (r < 0) return r;
  898.     r = (*dev->read)(f, where, nbytes);
  899.     if (r != nbytes) {
  900.         DEBUG(("load_region: unexpected EOF"));
  901.         return ENOEXEC;
  902.     }
  903.  
  904. /* now do the relocation */
  905. /* skip over symbol table, etc. */
  906.     r = (*dev->lseek)(f, sizeof(FILEHEAD) + fh->ftext + fh->fdata +
  907.             fh->fsym, SEEK_SET);
  908.     if (r < 0) return ENOEXEC;
  909.  
  910.     if (fh->reloc != 0 || (*dev->read)(f, (char *)&fixup, 4L) != 4L
  911.         || fixup == 0) {
  912.         return 0;    /* no relocation to be performed */
  913.     }
  914.  
  915.     size = LRBUFSIZ;
  916.     bytes_read = 0;
  917.     next = buffer;
  918.  
  919.     do {
  920.         if (fixup >= nbytes + start) {
  921.             TRACE(("load_region: end of relocation at %ld", fixup));
  922.             break;
  923.         }
  924.         else if (fixup >= start) {
  925.             reloc = *((long *)(where + fixup - start));
  926.             if (reloc < fh->ftext) {
  927.                 reloc += base->p_tbase;
  928.             } else if (reloc < fh->ftext + fh->fdata && base->p_dbase) {
  929.                 reloc += base->p_dbase - fh->ftext;
  930.             } else if (reloc < fh->ftext + fh->fdata + fh->fbss && base->p_bbase) {
  931.                 reloc += base->p_bbase - (fh->ftext + fh->fdata);
  932.             } else {
  933.                 DEBUG(("load_region: bad relocation: %ld", reloc));
  934.                 if (base->p_dbase)
  935.                     reloc += base->p_dbase - fh->ftext;    /* assume data reloc */
  936.                 else if (base->p_bbase)
  937.                     reloc += base->p_bbase - (fh->ftext + fh->fdata);
  938.                 else
  939.                     return ENOEXEC;
  940.             }
  941.             *((long *)(where + fixup - start)) = reloc;
  942.         }
  943.         do {
  944.             if (!bytes_read) {
  945.                 bytes_read =
  946.                     (*dev->read)(f,(char *)buffer,size);
  947.                 next = buffer;
  948.             }
  949.             if (bytes_read < 0) {
  950.                 DEBUG(("load_region: EOF in relocation"));
  951.                 return ENOEXEC;
  952.             }
  953.             else if (bytes_read == 0)
  954.                 c = 0;
  955.             else {
  956.                 c = *next++; bytes_read--;
  957.             }
  958.             if (c == 1) fixup += 254;
  959.         } while (c == 1);
  960.         fixup += ( (unsigned) c) & 0xff;
  961.     } while (c);
  962.  
  963.     return 0;
  964. }
  965.  
  966. /*
  967.  * function to check for existence of a shared text region
  968.  * corresponding to file "f", and if none is found, to create one
  969.  * the memory region being returned is attached to the current
  970.  * process
  971.  */
  972.  
  973. MEMREGION *
  974. get_text_seg(f, fh, xp)
  975.     FILEPTR *f;
  976.     FILEHEAD *fh;
  977.     XATTR *xp;
  978. {
  979.     SHTEXT *s;
  980.     MEMREGION *m;
  981.     long r;
  982.     BASEPAGE b;
  983.  
  984.     s = text_reg;
  985.  
  986.     while(s) {
  987.         if (samefile(&f->fc, &s->f->fc) &&
  988.             xp->mtime == s->mtime &&
  989.             xp->mdate == s->mdate)
  990.         {
  991.             m = s->text;
  992.             if (attach_region(curproc, m)) {
  993. TRACE(("re-using shared text region %lx", m));
  994.                 return m;
  995.             }
  996.             else {
  997.                 mint_errno = ENSMEM;
  998.                 return 0;
  999.             }
  1000.         }
  1001.         s = s->next;
  1002.     }
  1003.  
  1004. /* hmmm, not found; OK, we'll have to create a new text region */
  1005.  
  1006.     s = kmalloc(SIZEOF(SHTEXT));
  1007.     if (!s) {
  1008.         mint_errno = ENSMEM;
  1009.         return 0;
  1010.     }
  1011.     m = 0;
  1012. /* actually, I can't see why loading in TT RAM is ever undesireable,
  1013.  * since shared text programs should be very clean (and since only
  1014.  * the text segment is going in there). But better safe than sorry.
  1015.  */
  1016.     if (fh->flag & F_ALTLOAD) {
  1017.         m = addr2mem(alloc_region(alt, fh->ftext));
  1018.     }
  1019.     if (!m)
  1020.         m = addr2mem(alloc_region(core, fh->ftext));
  1021.  
  1022.     if (!m) {
  1023.         kfree(s);
  1024.         mint_errno = ENSMEM;
  1025.         return 0;
  1026.     }
  1027.  
  1028. /* set up a fake "basepage" for load_and_reloc
  1029.  * note: the 0 values should make load_and_reloc
  1030.  * barf on any attempts at data relocation, since we have
  1031.  * no data segment
  1032.  */
  1033. TRACE(("attempting to create shared text region"));
  1034.  
  1035.     b.p_tbase = m->loc;
  1036.     b.p_tlen = fh->ftext;
  1037.     b.p_dbase = 0;
  1038.     b.p_dlen = 0;
  1039.     b.p_bbase = b.p_blen = 0;
  1040.  
  1041.     r = load_and_reloc(f, fh, (char *)m->loc, 0, fh->ftext, &b);
  1042.     if (r) {
  1043.         m->links = 0;
  1044.         detach_region(curproc, m);
  1045.         kfree(s);
  1046.         return 0;
  1047.     }
  1048.  
  1049. /* region has valid shared text data */
  1050.     m->mflags |= M_SHTEXT;
  1051.  
  1052. /*
  1053.  * KLUDGE: to make sure we always have up to date shared text
  1054.  * info, even across a network, we leave the file passed
  1055.  * to us open with DENYWRITE mode, so that nobody will
  1056.  * modify it.
  1057.  */
  1058.     f->links++;    /* keep the file open longer */
  1059.  
  1060. /* BUG: what if someone already has the file open for
  1061.  * writing? Then we could get screwed...
  1062.  */
  1063.     f->flags = (f->flags & ~O_SHMODE) | O_DENYW;
  1064.     s->f = f;
  1065.     s->text = m;
  1066.     s->next = text_reg;
  1067.     s->mtime = xp->mtime;
  1068.     s->mdate = xp->mdate;
  1069.     text_reg = s;
  1070. TRACE(("shared text region %lx created", m));
  1071.     return m;
  1072. }
  1073.  
  1074. /*
  1075.  * exec_region(p, mem, thread): create a child process out of a mem region
  1076.  * "p" is the process structure set up by the parent; it may be "curproc",
  1077.  * if we're overlaying. "mem" is the loaded memory region returned by
  1078.  * "load region". Any open files (other than the standard handles) owned
  1079.  * by "p" are closed, and if thread !=0 all memory is released; the caller
  1080.  * must explicitly attach the environment and base region. The caller must
  1081.  * also put "p" on the appropriate queue (most likely READY_Q).
  1082.  */
  1083.  
  1084. extern long mint_dos(), mint_bios();
  1085.  
  1086. void rts() {}        /* dummy termination routine */
  1087.  
  1088. PROC *
  1089. exec_region(p, mem, thread)
  1090.     PROC      *p;
  1091.     MEMREGION *mem;
  1092.     int thread;
  1093. {
  1094.     BASEPAGE *b;
  1095.     FILEPTR *f;
  1096.     int i;
  1097.     MEMREGION *m;
  1098.  
  1099.     TRACE(("exec_region"));
  1100.  
  1101.     b = (BASEPAGE *) mem->loc;
  1102.  
  1103.     cpush((void *)b->p_tbase, b->p_tlen);    /* flush cached versions of the text */
  1104.     
  1105. /* set some (undocumented) variables in the basepage */
  1106.     b->p_defdrv = p->curdrv;
  1107.     for (i = 0; i < 6; i++)
  1108.         b->p_devx[i] = i;
  1109.  
  1110.     p->dta = (DTABUF *)(b->p_dta = &b->p_cmdlin[0]);
  1111.     p->base = b;
  1112.  
  1113. /* close extra open files */
  1114.     for (i = MIN_OPEN; i < MAX_OPEN; i++) {
  1115.         if ( (f = p->handle[i]) != 0 && (p->fdflags[i] & FD_CLOEXEC) ) {
  1116.             do_pclose(p, f);
  1117.             p->handle[i] = 0;
  1118.         }
  1119.     }
  1120.  
  1121. /* initialize memory */
  1122.     recalc_maxmem(p);
  1123.     if (p->maxmem) {
  1124.         shrink_region(mem, p->maxmem);
  1125.         b->p_hitpa = b->p_lowtpa + mem->len;
  1126.     }
  1127.  
  1128.     p->memflags = b->p_flags;
  1129.  
  1130.     if (!thread) {
  1131.         for (i = 0; i < p->num_reg; i++) {
  1132.             m = p->mem[i];
  1133.             if (m) {
  1134.                 m->links--;
  1135.                 if (m->links <= 0)
  1136.                     free_region(m);
  1137.             }
  1138.         }
  1139.         if (p->num_reg > NUM_REGIONS) {
  1140.             kfree(p->mem); kfree(p->addr);
  1141.             p->mem = kmalloc(NUM_REGIONS * SIZEOF(MEMREGION *));
  1142.             p->addr = kmalloc(NUM_REGIONS * SIZEOF(virtaddr));
  1143.     /* note: the mallocs have succeeded, since we just freed bigger areas */
  1144.             assert(p->mem && p->addr);
  1145.             p->num_reg = NUM_REGIONS;
  1146.         }
  1147.         zero((char *)p->mem, (p->num_reg)*SIZEOF(MEMREGION *));
  1148.         zero((char *)p->addr, (p->num_reg)*SIZEOF(virtaddr));
  1149.     }
  1150.  
  1151. /* initialize signals */
  1152.     p->sigmask = 0;
  1153.     for (i = 0; i < NSIG; i++) {
  1154.         if (p->sighandle[i] != SIG_IGN) {
  1155.             p->sighandle[i] = SIG_DFL;
  1156.             p->sigflags[i] = 0;
  1157.             p->sigextra[i] = 0;
  1158.         }
  1159.     }
  1160.  
  1161. /* zero the user registers, and set the FPU in a "clear" state */
  1162.     for (i = 0; i < 15; i++)
  1163.         p->ctxt[CURRENT].regs[i] = 0;
  1164.     p->ctxt[CURRENT].sr = 0;
  1165.     p->ctxt[CURRENT].fstate[0] = 0;
  1166.  
  1167. /* set PC, stack registers, etc. appropriately */
  1168.     p->ctxt[CURRENT].pc = b->p_tbase;
  1169.  
  1170. /* The "-0x20" is to make sure that syscall.s won't run past the end of
  1171.  * memory when the user makes a system call and doesn't push very many
  1172.  * parameters -- syscall always tries to copy the maximum possible number
  1173.  * of parms.
  1174.  *
  1175.  * NOTE: there's a sanity check here in case programs Mshrink a basepage
  1176.  * without fixing the p_hitpa field in the basepage; this is to ensure
  1177.  * compatibility with older versions of MiNT, which ignore p_hitpa.
  1178.  */
  1179.     if (valid_address(b->p_hitpa - 0x20))
  1180.         p->ctxt[CURRENT].usp = b->p_hitpa - 0x20;
  1181.     else
  1182.         p->ctxt[CURRENT].usp = mem->loc + mem->len - 0x20;
  1183.  
  1184.     p->ctxt[CURRENT].ssp = (long)(p->stack + ISTKSIZE);
  1185.     p->ctxt[CURRENT].term_vec = (long)rts;
  1186.  
  1187. /* set up stack for process */
  1188.     *((long *)(p->ctxt[CURRENT].usp + 4)) = (long) b;
  1189.  
  1190. /* check for a valid text region. some compilers (e.g. Lattice 3) just throw
  1191.    everything into the text region, including data; fork() must be careful
  1192.    to save the whole region, then. We assume that if the compiler (or
  1193.    assembler, or whatever) goes to the trouble of making separate text, data,
  1194.    and bss regions, then the text region is code and isn't modified and
  1195.    fork doesn't have to save it.
  1196.  */
  1197.     if (b->p_blen != 0 || b->p_dlen != 0)
  1198.         p->txtsize = b->p_tlen;
  1199.     else
  1200.         p->txtsize = 0;
  1201.  
  1202. /*
  1203.  * An ugly hack: dLibs tries to poke around in the parent's address space
  1204.  * to find stuff. For now, we'll allow this by faking a pointer into
  1205.  * the parent's address space in the place in the basepage where dLibs is
  1206.  * expecting it. This ugly hack only works correctly if the Pexec'ing
  1207.  * program (i.e. curproc) is in user mode.
  1208.  */
  1209.     if (curproc != rootproc)
  1210.         curproc->base->p_usp = curproc->ctxt[SYSCALL].usp - 0x32;
  1211.  
  1212.     return p;
  1213. }
  1214.  
  1215. /*
  1216.  * misc. utility routines
  1217.  */
  1218.  
  1219. /*
  1220.  * long memused(p): return total memory allocated to process p
  1221.  */
  1222.  
  1223. long
  1224. memused(p)
  1225.     PROC *p;
  1226. {
  1227.     int i;
  1228.     long size;
  1229.  
  1230.     size = 0;
  1231.     for (i = 0; i < p->num_reg; i++) {
  1232.         if (p->mem[i])
  1233.             size += p->mem[i]->len;
  1234.     }
  1235.     return size;
  1236. }
  1237.  
  1238. /* 
  1239.  * recalculate the maximum memory limit on a process; this limit depends
  1240.  * on the max. allocated memory and max. total memory limits set by
  1241.  * p_setlimit (see dos.c), and (perhaps) on the size of the program
  1242.  * that the process is executing. whenever any of these things
  1243.  * change (through p_exec or p_setlimit) this routine must be called
  1244.  */
  1245.  
  1246. void
  1247. recalc_maxmem(p)
  1248.     PROC *p;
  1249. {
  1250.     BASEPAGE *b;
  1251.     long siz;
  1252.  
  1253.     b = (BASEPAGE *)p->base;
  1254.     if (b)
  1255.         siz = b->p_tlen + b->p_dlen + b->p_blen;
  1256.     else
  1257.         siz = 0;
  1258.     p->maxmem = 0;
  1259.     if (p->maxdata) {
  1260.         p->maxmem = p->maxdata + siz;
  1261.     }
  1262.  
  1263.     if (p->maxcore) {
  1264.         if (p->maxmem == 0 || p->maxmem > p->maxcore)
  1265.             p->maxmem = p->maxcore;
  1266.     }
  1267.     if (p->maxmem && p->maxmem < siz)
  1268.         p->maxmem = siz;
  1269. }
  1270.  
  1271. /*
  1272.  * valid_address: checks to see if the indicated address falls within
  1273.  * memory attached to the current process
  1274.  */
  1275.  
  1276. int
  1277. valid_address(addr)
  1278.     long addr;
  1279. {
  1280.     int i;
  1281.     MEMREGION *m;
  1282.  
  1283.     for (i = 0; i < curproc->num_reg; i++) {
  1284.         if ((m = curproc->mem[i]) != 0) {
  1285.             if (addr >= m->loc && addr <= m->loc + m->len)
  1286.                 return 1;
  1287.         }
  1288.     }
  1289.     return 0;
  1290. }
  1291.  
  1292. /*
  1293.  * some debugging stuff
  1294.  */
  1295.  
  1296. void
  1297. DUMPMEM(map)
  1298.     MMAP map;
  1299. {
  1300. #ifndef NO_DEBUG_INFO
  1301.     MEMREGION *m;
  1302.  
  1303.     m = *map;
  1304.     ALERT("memory dump: starting at region %lx", m);
  1305.     while (m) {
  1306. ALERT("%ld bytes at %lx (%d links); next region %lx", m->len, m->loc,
  1307.       m->links, m->next);
  1308.     m = m->next;
  1309.     }
  1310. #endif
  1311. }
  1312.  
  1313. void
  1314. sanity_check(map)
  1315.     MMAP map;
  1316. {
  1317. #ifdef SANITY_CHECK
  1318.     MEMREGION *m, *nxt;
  1319.     long end;
  1320.  
  1321.     m = *map;
  1322.     while (m) {
  1323.         nxt = m->next;
  1324.         if (nxt) {
  1325.             end = m->loc + m->len;
  1326.             if (m->loc < nxt->loc && end > nxt->loc) {
  1327.                 FATAL("MEMORY CHAIN CORRUPTED");
  1328.             }
  1329.             else if (end == nxt->loc && ISFREE(m) && ISFREE(nxt)) {
  1330.                 ALERT("Continguous memory regions not merged!");
  1331.             }
  1332.         }
  1333.         m = nxt;
  1334.     }
  1335. #else
  1336.     UNUSED(map);
  1337. #endif
  1338. }
  1339.